/*****************************************************************************
 *
 * Atmel Corporation
 *
 * File              : JobInfo.cpp
 * Compiler          : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/
 * Revision          : $Revision: 1163 $
 * Date              : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $
 * Updated by        : $Author: ohlia $
 *
 * Support mail      : avr@atmel.com
 *
 * Target platform   : Win32
 *
 * AppNote           : AVR911 - AVR Open-source Programmer
 *
 * Description       : A class holding information on what the AVR Open-Source Programmer
 *                     should do. The information is derived from the command-line.
 *
 *
 ****************************************************************************/
#include "JobInfo.hpp"

#include <conio.h>

#define VERSIONSTRING "TMX Edition v 0.0.1" // For use in later text output.

#define TIMEOUT 10
#define TIMEOUTSTRING "10"


JobInfo::JobInfo()
{
        /* Initialize variables */
        showHelp = false;
        silentMode = false;
        noProgressIndicator = false;
        readSignature = false;
        chipErase = false;
        getHWrevision = false;
        getSWrevision = false;
        programFlash = false;
        programEEPROM = false;
        readFlash = false;
        readEEPROM = false;
        verifyFlash = false;
        verifyEEPROM = false;
        readLockBits = false;
        readFuseBits = false;
        readOSCCAL = false;

        deviceName.erase();
        inputFileFlash.erase();
        inputFileEEPROM.erase();
        outputFileFlash.erase();
        outputFileEEPROM.erase();

        OSCCAL_Parameter = -1;
        OSCCAL_FlashAddress = -1;
        OSCCAL_EEPROMAddress = -1;

        programLockBits = -1;
        verifyLockBits = -1;

        programFuseBits = -1;
        programExtendedFuseBits = -1;
        verifyFuseBits = -1;
        verifyExtendedFuseBits = -1;

        memoryFillPattern = -1;

        flashStartAddress = -1;
        flashEndAddress = -1;

        eepromStartAddress = -1;
        eepromEndAddress = -1;

        comPort = -1;
}



void JobInfo::parseCommandline( int argc, char *argv[] )
{
        char * param; // Temp string ptr for holding current parsed parameter.
        int comma; // Temp position for comma separator in address ranges.

        /* Get application directory */
        string ownpath = argv[0];
        int slash_pos = ownpath.find_last_of( "\\/" ); // Search for last og / or \.
        if( slash_pos == string::npos ) // Not found?
        {
                ownpath.assign( "." ); // The current directory is the AVROSP EXE path also.
        } else
        {
                ownpath.erase( slash_pos ); // Remove from and including the last slash separator.
        }

        searchpath.push_back( "." ); // Add current directory also.
        searchpath.push_back( ownpath ); // Save for later.

        if( argc <= 1 )
        {
                showHelp = true;
                return;
        }

        /* Iterate through cmdline parameters */
        for( int i = 1; i < argc; i++ )
        {
                param = argv[i];

                /* Allow parameters to start with '-' */
                if( param[0] != '-' )
                        throw new ErrorMsg( "All parameters must start with '-'!" );

                if( strlen( param ) <= 1 )
                        throw new ErrorMsg( "Parameters cannot be just the minus without any characters!" );

                /* Now for the parsing... */
                switch( param[1] )
                {
                        case 'a' : // Address range specified.
                                if( strlen( param ) <= 2 )
                                        throw new ErrorMsg( "Cannot use -a without memory type!" );

                                if( strlen( param ) <= 5 )
                                        throw new ErrorMsg( "Cannot use -a without start and end address!" );

                                /* Find comma position and set it to '0' to help hex conversion */
                                comma = 2;
                                while( (param[comma] != 0) && (param[comma] != ',') )
                                        comma++;

                                if( comma == strlen( param ) ) // No comma found?
                                        throw new ErrorMsg( "No comma separator found in -a parameter!" );

                                param[comma] = 0; // It is now two separate strings for hex conversion.

                                /* Convert limits */
                                switch( param[2] )
                                {
                                        case 'f' : // Flash address range.
                                                try
                                                {
                                                        flashStartAddress = convertHex( param + 3 );
                                                }
                                                catch( ErrorMsg * e )
                                                {
                                                        delete e;
                                                        throw new ErrorMsg( "Number format error in start limit for -af parameter!" );
                                                }

                                                try
                                                {
                                                        flashEndAddress = convertHex( param + comma + 1 );
                                                }
                                                catch( ErrorMsg * e )
                                                {
                                                        delete e;
                                                        throw new ErrorMsg( "Number format error in end limit for -af parameter!" );
                                                }

                                                if( flashEndAddress < flashStartAddress )
                                                        throw new ErrorMsg( "Cannot have Flash end limit less than start limit!" );

                                                break;

                                        case 'e' : // EEPROM address range.
                                                try
                                                {
                                                        eepromStartAddress = convertHex( param + 3 );
                                                }
                                                catch( ErrorMsg * e )
                                                {
                                                        delete e;
                                                        throw new ErrorMsg( "Number format error in start limit for -ae parameter!" );
                                                }

                                                try
                                                {
                                                        eepromEndAddress = convertHex( param + comma + 1 );
                                                }
                                                catch( ErrorMsg * e )
                                                {
                                                        delete e;
                                                        throw new ErrorMsg( "Number format error in end limit for -ae parameter!" );
                                                }

                                                if( eepromEndAddress < eepromStartAddress )
                                                        throw new ErrorMsg( "Cannot have EEPROM end limit less than start limit!" );

                                                break;

                                        default:
                                                throw new ErrorMsg( "Unknown choice for -a, use -af or -ae!" );

                                }

                                break;


                        case 'b' : // Get revision.
                                if( strlen( param ) != 3 )
                                        throw new ErrorMsg( "Specify SW og HW revision, not just -b!" );

                                switch( param[2] )
                                {
                                        case 'h' : // Hardware revision.
                                                getHWrevision = true;
                                                break;

                                        case 's' : // Software revision.
                                                getSWrevision = true;
                                                break;

                                        default:
                                                throw new ErrorMsg( "Unknown choice for -b, use -bs or -bh!" );
                                }

                                break;


                        case 'c' : // Specify COM port.
                                if( ! (strlen( param ) == 6 || strlen( param ) == 7) )
                                        throw new ErrorMsg( "COM port parameter syntax is -cCOMx or -cCOMxx !" );

                                if( param[2] != 'C' || param[3] != 'O' || param[4] != 'M' )
                                        throw new ErrorMsg( "COM port parameter syntax is -cCOMx!" );

                                if(strlen( param ) == 6)
                                    {
                                        if( param[5] < '1' || param[5] > '8' )
                                                throw new ErrorMsg( "Use COM1 to COM8!" );

                                        comPort = param[5] - '0'; // Convert COM port digit to number.
                                    }
                                if(strlen( param ) == 7)
                                    {
                                        if( param[5] < '1' || param[5] > '8' )
                                                throw new ErrorMsg( "Use COM1x to COM8x!" );
                                        if( param[6] < '0' || param[6] > '9' )
                                                throw new ErrorMsg( "Use COMx0 to COMx9!" );


                                        comPort = (param[5] - '0')*10 +(param[6] - '0'); // Convert COM port digit to number.
                                    }

                                break;


                        case 'd' : // Device name specified.
                                if( strlen( param ) <= 2 )
                                        throw new ErrorMsg( "Cannot use -d without the device name!" );

                                /* Copy device name string to class variable */
                                deviceName.assign( param + 2 );
                                break;


                        case 'e' : // Chip erase before programming.
                                if( strlen( param ) != 2 )
                                        throw new ErrorMsg( "Parameter -e needs no extra arguments!" );

                                chipErase = true;
                                break;


                        case 'E' : // Set extended fuse bits.
                                if( strlen( param ) != 4 )
                                        throw new ErrorMsg( "Use two hex digits for the -E parameter!" );

                                try
                                {
                                        programExtendedFuseBits = convertHex( param + 2 );
                                }
                                catch( ErrorMsg * e )
                                {
                                        delete e;
                                        throw new ErrorMsg( "Hex number format error for -E parameter!" );
                                }

                                break;


                        case 'f' : // Set fuse bits.
                                if( strlen( param ) != 6 )
                                        throw new ErrorMsg( "Use four hex digits for the -f parameter!" );

                                try
                                {
                                        programFuseBits = convertHex( param + 2 );
                                }
                                catch( ErrorMsg * e )
                                {
                                        delete e;
                                        throw new ErrorMsg( "Hex number format error for -f parameter!" );
                                }

                                break;


                        case 'F' : // Verify fuse bits.
                                if( strlen( param ) != 6 )
                                        throw new ErrorMsg( "Use four hex digits for the -F parameter!" );

                                try
                                {
                                        verifyFuseBits = convertHex( param + 2 );
                                }
                                catch( ErrorMsg * e )
                                {
                                        delete e;
                                        throw new ErrorMsg( "Hex number format error for -F parameter!" );
                                }

                                break;


                        case 'g' : // Silent operation.
                                if( strlen( param ) != 2 )
                                        throw new ErrorMsg( "Parameter -g needs no extra arguments!" );

                                silentMode = true;
                                break;


                        case 'G' : // Verify extended fuse bits.
                                if( strlen( param ) != 4 )
                                        throw new ErrorMsg( "Use two hex digits for the -G parameter!" );

                                try
                                {
                                        verifyExtendedFuseBits = convertHex( param + 2 );
                                }
                                catch( ErrorMsg * e )
                                {
                                        delete e;
                                        throw new ErrorMsg( "Hex number format error for -G parameter!" );
                                }

                                break;


                        case 'h' : // Help screen.
                        case '?' : // Help screen.
                                if( strlen( param ) != 2 )
                                        throw new ErrorMsg( "Parameter -h and -? needs no extra arguments!" );

                                showHelp = true;
                                break;


                        case 'i' : // Input file specified.
                                if( strlen( param ) <= 2 )
                                        throw new ErrorMsg( "Cannot use -i without memory type!" );

                                if( strlen( param ) <= 3 )
                                        throw new ErrorMsg( "Cannot use -i without file name!" );

                                /* Copy file name string to correct class variable */
                                switch( param[2] )
                                {
                                        case 'f' : // Flash input file.
                                                inputFileFlash.assign( param + 3 );
                                                break;

                                        case 'e' : // EEPROM input file.
                                                inputFileEEPROM.assign( param + 3 );
                                                break;

                                        default:
                                                throw new ErrorMsg( "Unknown choice for -i, use -if or -ie!" );
                                }

                                break;


                        case 'l' : // Set lock bits.
                                if( strlen( param ) != 4 )
                                        throw new ErrorMsg( "Use two hex digits for the -l parameter!" );

                                try
                                {
                                        programLockBits = convertHex( param + 2 );
                                }
                                catch( ErrorMsg * e )
                                {
                                        delete e;
                                        throw new ErrorMsg( "Hex number format error for -l parameter!" );
                                }

                                break;


                        case 'L' : // Verify lock bits.
                                if( strlen( param ) != 4 )
                                        throw new ErrorMsg( "Use two hex digits for the -l parameter!" );

                                try
                                {
                                        verifyLockBits = convertHex( param + 2 );
                                }
                                catch( ErrorMsg * e )
                                {
                                        delete e;
                                        throw new ErrorMsg( "Hex number format error for -L parameter!" );
                                }

                                break;


                        case 'o' : // Output file specified.
                                if( strlen( param ) <= 2 )
                                        throw new ErrorMsg( "Cannot use -o without memory type!" );

                                if( strlen( param ) <= 3 )
                                        throw new ErrorMsg( "Cannot use -o without file name!" );

                                /* Copy file name string to correct class variable */
                                switch( param[2] )
                                {
                                        case 'f' : // Flash output file.
                                                outputFileFlash.assign( param + 3 );
                                                break;

                                        case 'e' : // EEPROM output file.
                                                outputFileEEPROM.assign( param + 3 );
                                                break;

                                        default:
                                                throw new ErrorMsg( "Unknown choice for -o, use -of or -oe!" );
                                }

                                break;


                        case 'O' : // Read OSCCAL byte.
                                switch( strlen( param ) )
                                {
                                        case 2 : // No value specified, use first OSCCAL byte.
                                                readOSCCAL = true;
                                                OSCCAL_Parameter = 0; // First OSCCAL byte.
                                                break;

                                        case 3 : // Byte index specified.
                                        case 4 :
                                                readOSCCAL = true;
                                                try
                                                {
                                                        OSCCAL_Parameter = convertHex( param + 2 );
                                                }
                                                catch( ErrorMsg * e )
                                                {
                                                        delete e;
                                                        throw new ErrorMsg( "Hex number format error for -O parameter!" );
                                                }

                                                break;


                                        case 5 : // Direct value specified.
                                                if( param[2] != '#' )
                                                        throw new ErrorMsg( "Use one or two hex digits for -O and two for -O#!" );

                                                readOSCCAL = false;
                                                try
                                                {
                                                        OSCCAL_Parameter = convertHex( param + 3 );
                                                }
                                                catch( ErrorMsg * e )
                                                {
                                                        delete e;
                                                        throw new ErrorMsg( "Hex number format error for -O# parameter!" );
                                                }

                                                break;

                                        default:
                                                throw new ErrorMsg( "Invalid use of -O or -O# parameter!" );
                                }

                                break;


                        case 'p' : // Program data.
                                if( strlen( param ) != 3 )
                                        throw new ErrorMsg( "Specify memory type, not just -p!" );

                                switch( param[2] )
                                {
                                        case 'f' : // Program Flash memory.
                                                programFlash = true;
                                                break;

                                        case 'e' : // Program EEPROM memory.
                                                programEEPROM = true;
                                                break;

                                        case 'b' : // Both.
                                                programFlash = true;
                                                programEEPROM = true;
                                                break;

                                        default:
                                                throw new ErrorMsg( "Unknown choice for -p, use -pf, -pe or -pb!" );
                                }

                                break;


                        case 'q' : // Read all fuse bits.
                                if( strlen( param ) != 2 )
                                        throw new ErrorMsg( "Parameter -q needs no extra arguments!" );

                                readFuseBits = true;
                                break;


                        case 'r' : // Read data.
                                if( strlen( param ) != 3 )
                                        throw new ErrorMsg( "Specify memory type, not just -r!" );

                                switch( param[2] )
                                {
                                        case 'f' : // Read Flash memory.
                                                readFlash = true;
                                                break;

                                        case 'e' : // Read EEPROM memory.
                                                readEEPROM = true;
                                                break;

                                        case 'b' : // Both.
                                                readFlash = true;
                                                readEEPROM = true;
                                                break;

                                        default:
                                                throw new ErrorMsg( "Unknown choice for -r, use -rf, -re or -rb!" );
                                }

                                break;


                        case 's' : // Read signature byte.
                                if( strlen( param ) != 2 )
                                        throw new ErrorMsg( "Parameter -s needs no extra arguments!" );

                                readSignature = true;
                                break;


                        case 'S' : // Write OSCCAL byte to memory.
                                if( strlen( param ) <= 2 )
                                        throw new ErrorMsg( "Cannot use -S without memory type!" );

                                if( strlen( param ) <= 3 )
                                        throw new ErrorMsg( "Cannot use -S without byte address!" );

                                switch( param[2] )
                                {
                                        case 'f' : // Write to Flash address.
                                                try
                                                {
                                                        OSCCAL_FlashAddress = convertHex( param + 3 );
                                                }
                                                catch( ErrorMsg * e )
                                                {
                                                        delete e;
                                                        throw new ErrorMsg( "Cannot convert hex number for -Sf parameter!" );
                                                }
                                                break;

                                        case 'e' : // Write to EEPROM address.
                                                try
                                                {
                                                        OSCCAL_EEPROMAddress = convertHex( param + 3 );
                                                }
                                                catch( ErrorMsg * e )
                                                {
                                                        delete e;
                                                        throw new ErrorMsg( "Cannot convert hex number for -Se parameter!" );
                                                }
                                                break;

                                        default:
                                                throw new ErrorMsg( "Unknown choice for -S, use -Sf or -Se!" );
                                }

                                break;


                        case 'v' : // Verify data.
                                if( strlen( param ) != 3 )
                                        throw new ErrorMsg( "Specify memory type, not just -v!" );

                                switch( param[2] )
                                {
                                        case 'f' : // Verify Flash memory.
                                                verifyFlash = true;
                                                break;

                                        case 'e' : // Verify EEPROM memory.
                                                verifyEEPROM = true;
                                                break;

                                        case 'b' : // Both.
                                                verifyFlash = true;
                                                verifyEEPROM = true;
                                                break;

                                        default:
                                                throw new ErrorMsg( "Unknown choice for -v, use -vf, -ve or -vb!" );
                                }

                                break;


                        case 'x' : // Fill unspecified memory.
                                if( strlen( param ) != 4 )
                                        throw new ErrorMsg( "Use two hex digits for the -x parameter!" );

                                try
                                {
                                        memoryFillPattern = convertHex( param + 2 );
                                }
                                catch( ErrorMsg * e )
                                {
                                        delete e;
                                        throw new ErrorMsg( "Hex number format error for -x parameter!" );
                                }

                                break;


                        case 'y' : // Read lock bits.
                                if( strlen( param ) != 2 )
                                        throw new ErrorMsg( "Parameter -y needs no extra arguments!" );

                                readLockBits = true;
                                break;


                        case 'z' : // No progress indicator?
                                if( strlen( param ) != 2 )
                                        throw new ErrorMsg( "Parameter -z needs no extra arguments!" );

                                noProgressIndicator = true;
                                break;


                        default:
                                throw new ErrorMsg( "Unknow parameter!" );
                }
        }
}


void JobInfo::help()
{
        cout
                << "Command Line Switches:" << endl
                << "        [-d device name] [-if infile] [-ie infile] [-of outfile] [-oe outfile]" << endl
                << "        [-s] [-O index] [-O#value] [-Sf addr] [-Se addr] [-e] [-p f|e|b]" << endl
                << "        [-r f|e|b] [-v f|e|b] [-l value] [-L value] [-y] [-f value] [-E value]" << endl
                << "        [-F value] [-G value] [-q] [-x value] [-af start,stop] [-ae start,stop]" << endl
                << "        [-c port] [-b h|s] [-g] [-z] [-h|?]" << endl
                << endl
                << "Parameters:" << endl
                << "d       Device name. Must be applied when programming the device." << endl
                << "if      Name of FLASH input file. Required for programming or verification" << endl
                << "        of the FLASH memory. The file format is Intel Extended HEX." << endl
                << "ie      Name of EEPROM input file. Required for programming or verification" << endl
                << "        of the EEPROM memory. The file format is Intel Extended HEX." << endl
                << "of      Name of FLASH output file. Required for readout of the FLASH memory." << endl
                << "        The file format is Intel Extended HEX." << endl
                << "oe      Name of EEPROM output file. Required for readout of the EEPROM" << endl
                << "        memory. The file format is Intel Extended HEX." << endl
                << "s       Read signature bytes." << endl;
        getch();
        cout
                << "O       Read oscillator calibration byte. 'index' is optional." << endl
                << "O#      User-defined oscillator calibration value." << endl
                << "Sf      Write oscillator cal. byte to FLASH memory. 'addr' is byte address." << endl
                << "Se      Write oscillator cal. byte to EEPROM memory. 'addr' is byte address." << endl
                << "e       Erase device. If applied with another programming parameter, the" << endl
                << "        device will be erased before any other programming takes place." << endl
                << "p       Program device; FLASH (f), EEPROM (e) or both (b). Corresponding" << endl
                << "        input files are required." << endl
                << "r       Read out device; FLASH (f), EEPROM (e) or both (b). Corresponding" << endl
                << "        output files are required" << endl
                << "v       Verify device; FLASH (f), EEPROM (e) or both (b). Can be used with" << endl
                << "        -p or alone. Corresponding input files are required." << endl
                << "l       Set lock byte. 'value' is an 8-bit hex. value." << endl
                << "L       Verify lock byte. 'value' is an 8-bit hex. value to verify against." << endl
                << "y       Read back lock byte." << endl
                << "f       Set fuse bytes. 'value' is a 16-bit hex. value describing the" << endl
                << "        settings for the upper and lower fuse bytes." << endl
                << "E       Set extended fuse byte. 'value' is an 8-bit hex. value describing the" << endl
                << "        extend fuse settings." << endl
                << "F       Verify fuse bytes. 'value' is a 16-bit hex. value to verify against." << endl;
        getch();
        cout
                << "G       Verify extended fuse byte. 'value' is an 8-bit hex. value to" << endl
                << "        verify against." << endl
                << "q       Read back fuse bytes." << endl
                << "x       Fill unspecified locations with a value (00-ff). The default is" << endl
                << "        to not program locations not specified in the input files." << endl
                << "af      FLASH address range. Specifies the address range of operations. The" << endl
                << "        default is the entire FLASH. Byte addresses in hex." << endl
                << "ae      EEPROM address range. Specifies the address range of operations." << endl
                << "        The default is the entire EEPROM. Byte addresses in hex." << endl
                << "c       Select communication port; 'COM1' to 'COM8'. If this parameter is" << endl
                << "        omitted the program will scan the COM ports for a programmer." << endl
                << "b       Get revisions; hardware revision (h) and software revision (s)." << endl
                << "g       Silent operation." << endl
                << "z       No progress indicator. E.g. if piping to a file for log purposes, use" << endl
                << "        this option to avoid the characters used for the indicator." << endl
                << "h|?     Help information (overrides all other settings)." << endl
                << endl
                << "Example:" << endl
                << "        AVROSP -dATmega128 -ifmyapp.hex -pf" << endl;
}


long JobInfo::convertHex( char * txt )
{
        string t( txt );
        return Util.convertHex( t );
}


bool JobInfo::doJob()
{
        bool res = true;
        long scanCOM;
        SerialPort * com;
        AVRProgrammer * prog;
        AVRDevice * avr;
        string programmerID;
        long sig0, sig1, sig2; // Signature bytes.

        /* Set correct silent and progress indicator status */
        if( silentMode )
        {
                Util.muteLog();
                Util.muteProgress(); // Silent also includes progress indicator.
        }

        if( noProgressIndicator )
                Util.muteProgress();

        /* Application header text */
        Util.log( "AVR Open-source Programmer " VERSIONSTRING " (C) 2011 TMX Corp.\n\r\n\r" );

        /* Show help screen? */
        if( showHelp )
        {
                help();
                return true;
        }

        Util.log( "Serial port timeout set to " TIMEOUTSTRING " sec.\r\n" );

        /* Need to scan for COM port? */
        if( comPort == -1 )
        {
                Util.log( "Scanning COM ports for supported programmer...\n\r" );

                for( scanCOM = 1; scanCOM <= 19; scanCOM++ )
                {
                        Util.progress( "COM" + Util.convertLong( scanCOM ) + "...\r\n" );

                        try
                        {
                                /* Try to communicate */
                                com = NULL;
                                com = new SerialPort( scanCOM, TIMEOUT );
                                com->openChannel();
                                programmerID = AVRProgrammer::readProgrammerID( com );

                                /* Contact! Check ID... Add custom handler signatures here */
                                if( programmerID == "AVRBOOT" || programmerID == "AVR ISP" )
                                {
                                        break;
                                }

                                delete com;
                                Util.progress( programmerID + " found - not supported!\r\n" );
                        }
                        catch( ErrorMsg * e )
                        {
                                /* No contact on COM port, skip to next */
                                if( com != NULL ) delete com;
                                delete e;
                        }
                }

                /* Exit if no supported programmers found */
                if( scanCOM > 8 )
                {
                        Util.log( "No supported programmers found!\r\n" );
                        return false;
                }

                comPort = scanCOM;

        } else // ... COM port is specified
        {
                /* Try to communicate, errors will propagate to caller */
                com = new SerialPort( comPort, TIMEOUT );
                com->openChannel();
                programmerID = AVRProgrammer::readProgrammerID( com );

                /* Contact! Check ID */
                if( programmerID != "AVRBOOT" && programmerID != "AVR ISP" )
                        throw new ErrorMsg( "Programmer not supported!" );
        }

        Util.log( "Found " + programmerID + " on COM" + Util.convertLong( comPort ) + "!\r\n" );

        /* Create programmer interface object, add custom handlers here */
        if( programmerID == "AVRBOOT" )
        {
                prog = new AVRBootloader( com );
        }

        if( programmerID == "AVR ISP" )
        {
                prog = new AVRInSystemProg( com );
        }

        Util.log( "Entering programming mode...\r\n" );
        prog->enterProgrammingMode(); // Ignore return code.

        /* Do device independent operations */
        doDeviceIndependent( prog );

        /* Finished if no device name is specified */
        if( deviceName.size() == 0 )
        {
                Util.log( "Device name not specified!\r\n" );
                return false;
        }

        /* Parse XML part description file */
        avr = new AVRDevice( deviceName );
        Util.log( "Parsing XML file for device parameters...\r\n" );
        Util.parsePath( searchpath );
        avr->readParametersFromAVRStudio( searchpath );

        /* Verify that the device signature matches the signature from the XML file */
        avr->getSignature( &sig0, &sig1, &sig2 );
        if( prog->checkSignature( sig0, sig1, sig2 ) )
                Util.log( "Signature matches device!\r\n" );

        /* Do device dependent operations */
        res = doDeviceDependent( prog, avr );

        /* Clean up */
        Util.log( "Leaving programming mode...\r\n" );
        prog->leaveProgrammingMode(); // Ignore return code.

        delete avr;
        delete prog;
        delete com;

        return res;
}


void JobInfo::doDeviceIndependent( AVRProgrammer * prog )
{
        long sig0, sig1, sig2; // Signature bytes.
        long minor, major; // Minor and major programmer revision.

        /* Read signature? */
        if( readSignature )
        {
                Util.log( "Reading signature bytes: " );
                if( !prog->readSignature( &sig0, &sig1, &sig2 ) )
                        throw new ErrorMsg( "Signature readout is not supported by this programmer!" );

                /* No pass through Util, since user wants the info */
                cout.fill( '0' );
                cout << hex
                        << "0x" << setw(2) << sig0 << " "
                        << "0x" << setw(2) << sig1 << " "
                        << "0x" << setw(2) << sig2 << endl;
        }

        /* Get software version? */
        if( getSWrevision )
        {
                Util.log( "Reading programmer software revision: " );
                if( !prog->programmerSoftwareVersion( &major, &minor ) )
                        throw new ErrorMsg( "Software revision readout is not supported by this programmer!" );

                /* No pass through Util, since user wants the info */
                cout << (char) (major & 0xff) << "." << (char) (minor & 0xff) << endl;
        }

        /* Get software version? */
        if( getHWrevision )
        {
                Util.log( "Reading programmer hardware revision: " );
                if( !prog->programmerHardwareVersion( &major, &minor ) )
                        throw new ErrorMsg( "Hardware revision readout is not supported by this programmer!" );

                /* No pass through Util, since user wants the info */
                cout << (char) (major & 0xff) << "." << (char) (minor & 0xff) << endl;
        }
}


bool JobInfo::doDeviceDependent( AVRProgrammer * prog, AVRDevice * avr )
{
        bool res = true;
        HEXFile * hex;
        HEXFile * hex_v; // Used for verifying memory contents.
        long pos; // Used when comparing data.
        long bits; // Used for lock and fuse bits.

        /* Set programmer pagesize */
        prog->setPagesize( avr->getPageSize() );

        /* Check if specified address limits are within device range */
        if( flashEndAddress != -1 )
        {
                if( flashEndAddress >= avr->getFlashSize() )
                        throw new ErrorMsg( "Specified Flash address range is outside device address space!" );
        } else
        {
                flashStartAddress = 0;
                flashEndAddress = avr->getFlashSize() - 1;
        }

        if( eepromEndAddress != -1 )
        {
                if( eepromEndAddress >= avr->getEEPROMSize() )
                        throw new ErrorMsg( "Specified EEPROM address range is outside device address space!" );
        } else
        {
                eepromStartAddress = 0;
                eepromEndAddress = avr->getEEPROMSize() - 1;
        }


        /* Read out Flash contents? */
        if( readFlash )
        {
                /* Check that filename has been specified */
                if( outputFileFlash.size() == 0 )
                        throw new ErrorMsg( "Cannot read Flash without output file specified!" );

                /* Prepare the file */
                hex = new HEXFile( avr->getFlashSize() );
                hex->setUsedRange( flashStartAddress, flashEndAddress );

                /* Read data and save file */
                Util.log( "Reading Flash contents...\r\n" );
                if( !prog->readFlash( hex ) )
                        throw new ErrorMsg( "Flash readout is not supported by this programmer!" );
                Util.log( "Writing HEX output file...\r\n" );
                hex->writeFile( outputFileFlash );

                delete hex;
        }


        /* Read out EEPROM contents? */
        if( readEEPROM )
        {
                /* Check that filename has been specified */
                if( outputFileEEPROM.size() == 0 )
                        throw new ErrorMsg( "Cannot read EEPROM without output file specified!" );

                /* Prepare the file */
                hex = new HEXFile( avr->getEEPROMSize() );
                hex->setUsedRange( eepromStartAddress, eepromEndAddress );

                /* Read data and save file */
                Util.log( "Reading EEPROM contents...\r\n" );
                if( !prog->readEEPROM( hex ) )
                        throw new ErrorMsg( "EEPROM readout is not supported by this programmer!" );
                Util.log( "Writing HEX output file...\r\n" );
                hex->writeFile( outputFileEEPROM );

                delete hex;
        }


        /* Read lock bits? */
        if( readLockBits )
        {
                Util.log( "Reading lock bits...\r\n" );
                if( !prog->readLockBits( &bits ) )
                        throw new ErrorMsg( "Lock bit readout is not supported by this programmer!" );
                cout << "0x" << std::hex << setw(2) << bits << endl;
        }


        /* Read fuse bits (both ordinary and extended)? */
        if( readFuseBits )
        {
                if( !avr->getFuseStatus() )
                        throw new ErrorMsg( "Selected device has no fuse bits!" );

                Util.log( "Reading fuse bits...\r\n" );
                if( !prog->readFuseBits( &bits ) )
                        throw new ErrorMsg( "Fuse bit readout is not supported by this programmer!" );
                cout << "0x" << std::hex << setw(4) << bits << endl;

                if( avr->getXFuseStatus() )
                {
                        if( !prog->readExtendedFuseBits( &bits ) )
                                throw new ErrorMsg( "Extended fuse bit readout is not supported by this programmer!" );
                        cout << "0x" << std::hex << setw(2) << bits << endl;
                }
        }


        /* Erase chip before programming anything? */
        if( chipErase )
        {
                Util.log( "Erasing chip contents...\r\n" );
                if( !prog->chipErase() )
                        throw new ErrorMsg( "Chip erase is not supported by this programmer!" );
        }


        /* Prepare input hex file for flash */
        if( programFlash || verifyFlash )
        {
                /* Check that filename has been specified */
                if( inputFileFlash.size() == 0 )
                        throw new ErrorMsg( "Cannot program or verify Flash without input file specified!" );

                /* Prepare the file */
                hex = new HEXFile( avr->getFlashSize() );

                /* Fill if wanted */
                if( memoryFillPattern != -1 )
                        hex->clearAll( memoryFillPattern );

                /* Read file */
                Util.log( "Reading HEX input file for flash operations...\r\n" );
                hex->readFile( inputFileFlash );
                hex->writeFile("file_prog.hex");

                /* Check limits */
                if( hex->getRangeStart() > flashEndAddress ||
                                hex->getRangeEnd() < flashStartAddress )
                        throw new ErrorMsg( "HEX file defines data outside specified range!" );

                if( memoryFillPattern == -1 )
                {
                        if( hex->getRangeStart() > flashStartAddress )
                                flashStartAddress = hex->getRangeStart();

                        if( hex->getRangeEnd() < flashEndAddress )
                                flashEndAddress = hex->getRangeEnd();
                }

                hex->setUsedRange( flashStartAddress, flashEndAddress );
        }


        /* Program new Flash contents? */
        if( programFlash )
        {
                /* Program data */
                Util.log( "Programming Flash contents...\r\n" );
                if( !prog->writeFlash( hex ) )
                        throw new ErrorMsg( "Flash programming is not supported by this programmer!" );
        }


        /* Verify Flash contents? */
        if( verifyFlash )
        {
                /* Prepare HEX file for comparision */
                hex_v = new HEXFile( avr->getFlashSize() );

                /* Compare to Flash */
                Util.log( "Reading Flash contents...\r\n" );
                hex_v->setUsedRange( hex->getRangeStart(), hex->getRangeEnd() );
                if( !prog->readFlash( hex_v ) )
                        throw new ErrorMsg( "Flash readout is not supported by this programmer!" );

                /* Compare data */
                Util.log( "Comparing Flash data...\r\n" );

                for( pos = hex->getRangeStart(); pos <= hex->getRangeEnd(); pos++ )
                {
                        if( hex->getData( pos ) != hex_v->getData( pos ) )
                        {
                                cout << "Flash unequal at address 0x" << std::hex << pos << "!" <<  endl;
                                cout << "Read :"<<hex_v->getData( pos )<<" , must : "<<hex->getData( pos ) <<endl;
                                res = false;
                                break;
                        }
                }

                if( pos > hex->getRangeEnd() ) // All equal?
                {
                        Util.log( "Flash equal !\r\n" );
                }

                delete hex_v;
        }

        if( programFlash || verifyFlash )
                delete hex;


        /* Prepare input hex file for EEPROM */
        if( programEEPROM || verifyEEPROM )
        {
                /* Check that filename has been specified */
                if( inputFileEEPROM.size() == 0 )
                        throw new ErrorMsg( "Cannot program or verify EEPROM without input file specified!" );

                /* Prepare the file */
                hex = new HEXFile( avr->getEEPROMSize() );

                /* Fill if wanted */
                if( memoryFillPattern != -1 )
                        hex->clearAll( memoryFillPattern );

                /* Read file and program contents */
                Util.log( "Reading HEX input file for EEPROM operations...\r\n" );
                hex->readFile( inputFileEEPROM );

                /* Check limits */
                if( hex->getRangeStart() > eepromEndAddress ||
                                hex->getRangeEnd() < eepromStartAddress )
                        throw new ErrorMsg( "HEX file defines data outside specified range!" );

                if( memoryFillPattern == -1 )
                {
                        if( hex->getRangeStart() > eepromStartAddress )
                                eepromStartAddress = hex->getRangeStart();

                        if( hex->getRangeEnd() < eepromEndAddress )
                                eepromEndAddress = hex->getRangeEnd();
                }

                hex->setUsedRange( eepromStartAddress, eepromEndAddress );
        }


        /* Program new EEPROM contents? */
        if( programEEPROM )
        {
                /* Program data */
                Util.log( "Programming EEPROM contents...\r\n" );
                if( !prog->writeEEPROM( hex ) )
                        throw new ErrorMsg( "EEPROM programming is not supported by this programmer!" );
        }

        /* Verify EEPROM contents? */
        if( verifyEEPROM )
        {
                /* Prepare HEX file for comparision */
                hex_v = new HEXFile( avr->getEEPROMSize() );

                /* Compare to EEPROM */
                Util.log( "Reading EEPROM contents...\r\n" );
                hex_v->setUsedRange( hex->getRangeStart(), hex->getRangeEnd() );
                if( !prog->readEEPROM( hex_v ) )
                        throw new ErrorMsg( "EEPROM readout is not supported by this programmer!" );

                /* Compare data */
                Util.log( "Comparing EEPROM data...\r\n" );
                for( pos = hex->getRangeStart(); pos <= hex->getRangeEnd(); pos++ )
                {
                        if( hex->getData( pos ) != hex_v->getData( pos ) )
                        {
                                cout << "EEPROM unequal at address 0x" << std::hex << pos << "!" << endl;
                                res = false;
                                break;
                        }
                }

                if( pos > hex->getRangeEnd() ) // All equal?
                {
                        Util.log( "EEPROM equal !\r\n" );
                }

                delete hex_v;
        }

        if( programEEPROM || verifyEEPROM )
                delete hex;


        /* Program lock bits */
        if( programLockBits != -1 )
        {
                Util.log( "Programming lock bits...\r\n" );
                if( !prog->writeLockBits( programLockBits ) )
                        throw new ErrorMsg( "Lock bit programming is not supported by this programmer!" );
        }


        /* Program fuse bits */
        if( programFuseBits != -1 )
        {
                if( !avr->getFuseStatus() )
                        throw new ErrorMsg( "Selected device has no fuse bits!" );

                Util.log( "Programming fuse bits...\r\n" );
                if( !prog->writeFuseBits( programFuseBits ) )
                        throw new ErrorMsg( "Fuse bit programming is not supported by this programmer!" );
        }


        /* Program extended fuse bits */
        if( programExtendedFuseBits != -1 )
        {
                if( !avr->getXFuseStatus() )
                        throw new ErrorMsg( "Selected device has no extended fuse bits!" );

                Util.log( "Programming extended fuse bits...\r\n" );
                if( !prog->writeExtendedFuseBits( programExtendedFuseBits ) )
                        throw new ErrorMsg( "Extended fuse bit programming is not supported by this programmer!" );
        }


        /* Verify lock bits */
        if( verifyLockBits != -1 )
        {
                Util.log( "Verifying lock bits...\r\n" );
                if( !prog->readLockBits( &bits ) )
                        throw new ErrorMsg( "Lock bit readout is not supported by this programmer!" );
                if( bits == verifyLockBits )
                        cout << "Equal!" << endl;
                else
                        cout << "Unequal!" << endl;
        }


        /* Verify fuse bits */
        if( verifyFuseBits != -1 )
        {
                if( !avr->getFuseStatus() )
                        throw new ErrorMsg( "Selected device has no fuse bits!" );

                Util.log( "Verifying fuse bits...\r\n" );
                if( !prog->readFuseBits( &bits ) )
                        throw new ErrorMsg( "Fuse bit readout is not supported by this programmer!" );
                if( bits == verifyFuseBits )
                        cout << "Equal!" << endl;
                else
                    {
                        cout << "Unequal!" << endl;
                        res = false;
                    }


        }


        /* Verify extended fuse bits */
        if( verifyExtendedFuseBits != -1 )
        {
                if( !avr->getXFuseStatus() )
                        throw new ErrorMsg( "Selected device has no extended fuse bits!" );

                Util.log( "Verifying extended fuse bits...\r\n" );
                if( !prog->readExtendedFuseBits( &bits ) )
                        throw new ErrorMsg( "Extended fuse bit readout is not supported by this programmer!" );
                if( bits == verifyExtendedFuseBits )
                        cout << "Equal!" << endl;
                else
                    {
                        cout << "Unequal!" << endl;
                        res = false;
                    }
        }


        /* Read osccal value? */
        if( OSCCAL_Parameter != -1 )
        {
                /* Output to log if read from device */
                if( readOSCCAL )
                {
                        Util.log( "Reading OSCCAL from device...\r\n" );
                        pos = OSCCAL_Parameter;
                        if( !prog->readOSCCAL( pos, &OSCCAL_Parameter ) )
                                throw new ErrorMsg( "OSCCAL parameter readout is not supported by this programmer!" );
                        cout << "0x" << std::hex << setw(2) << OSCCAL_Parameter << endl;
                }
        }


        /* Write OSCCAL to Flash? */
        if( OSCCAL_FlashAddress != -1 )
        {
                if( OSCCAL_Parameter == -1 )
                        throw new ErrorMsg( "OSCCAL value not specified!" );

                Util.log( "Programming OSCCAL value to Flash...\r\n" );
                if( !prog->writeFlashByte( OSCCAL_FlashAddress, OSCCAL_Parameter ) )
                        throw new ErrorMsg( "Flash programming is not supported by this programmer!" );
        }


        /* Write OSCCAL to EEPROM? */
        if( OSCCAL_EEPROMAddress != -1 )
        {
                if( OSCCAL_Parameter == -1 )
                        throw new ErrorMsg( "OSCCAL value not specified!" );

                Util.log( "Programming OSCCAL value to EEPROM...\r\n" );
                if( !prog->writeEEPROMByte( OSCCAL_EEPROMAddress, OSCCAL_Parameter ) )
                        throw new ErrorMsg( "EEPROM programming is not supported by this programmer!" );
        }

        return res;
}


/* end of file */

